<script lang="tsx">
import {API} from "@/global";
import {ElButton, ElDialog, ElForm, ElMessage, ElNotification, ElProgress, UploadProps, UploadRequestOptions} from "element-plus";
import {defineComponent, PropType, reactive, Ref, ref} from "vue";
import Editor from "@/components/editor/editor.vue";
import WEditor from "@/components/weditor/weditor.vue";
import {Delete, Folder, Plus, ZoomIn} from "@element-plus/icons";
import api from "@/api";
import _ from "lodash";
import Map from "@/components/map/map.vue";

let formRef: Ref<any> = ref();

export default defineComponent({
  props: {
    footerShow: {type: Boolean, default: true, description: '是否显示footer'},
    isDialog: {type: Boolean, default: true, description: '是否显示dialog'},
    show: {type: Boolean, default: false},
    title: {type: String, default: '标题'},
    width: {type: Number, default: 1250},
    labelWidth: {type: Number, default: 80},
    beforeClose: {type: Function, default: () => { }},
    disabled: {type: Boolean as PropType<boolean>, default: false, description: '是否禁用'},
    form: {type: Object as PropType<API.IParams>, default: () => ({}), description: '表单配置'},
    formFn: {type: Array as PropType<IPopup.IFormFn[]>, default: [], description: '表单函数'},
    formShow: {type: Boolean as PropType<boolean>, default: false, description: '是否显示表单'},
    submitClick: {type: Function as PropType<(closeLoading: any) => void>, default: () => { }, description: '提交按钮点击事件'},
  },


  setup(props, {emit}: { emit: any }) {

    const _getDeepByPath = (obj: any, path: any) => {
      return path.split('.').reduce((acc: any, key: any) => acc[key], obj)
    }

    const resetFields = () => {
      formRef.value.resetFields()
    }


    const inputMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-input
                type={fn.fieldType}
                v-model={props.form[fn.key!]}
                value={_getDeepByPath(props.form, fn.key!)}
                disabled={fn.disabled}
                maxlength={fn.maxlength}
                show-word-limit={fn.showWordLimit}
                placeholder={fn.placeholder}

            />
          </el-form-item>
      )
    }


    const passwordMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-input
                v-model={props.form[fn.key!]}
                disabled={fn.disabled}
                show-password={true}
                maxlength={fn.maxlength}
                show-word-limit={fn.showWordLimit}
                placeholder={fn.placeholder}
                type="password"
            />
          </el-form-item>
      )
    }


    const textareaMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-input
                v-model={props.form[fn.key!]}
                disabled={fn.disabled}
                value={_getDeepByPath(props.form, fn.key!)}
                maxlength={fn.maxlength}
                show-word-limit={fn.showWordLimit}
                placeholder={fn.placeholder}
                type="textarea"
            />
          </el-form-item>
      )
    }

    const markdownMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <Editor
                see={props.disabled}
                form={props.form}
                fieldKey={fn.key!}
            />
          </el-form-item>
      )
    }

    const editorMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <WEditor
                see={props.disabled}
                form={props.form}
                fieldKey={fn.key!}
            />
          </el-form-item>
      )
    }

    const switchMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-switch
                v-model={props.form[fn.key!]}
                disabled={fn.disabled}
            />
          </el-form-item>
      )
    }

    const radioMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-radio-group v-model={props.form[fn.key!]} disabled={fn.disabled}>
              {
                fn.radioConfig!.data.map((item) => {
                  return (
                      <el-radio label={item.value} disabled={item.disabled}>{item.text}</el-radio>
                  )
                })
              }
            </el-radio-group>
          </el-form-item>
      )
    }

    const uploadMapFn = <T extends IPopup.IFormFn>(fn: T) => {

      props.form[fn.key!] = props.form[fn.key!] || [];

      const _cacheState = reactive({
        previewVisible: false,
        progress: 0,     // 上传进度
        status: '待上传',
        statusDot: 'i-default',
      })

      const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
        const _typesArray = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp']
        if (_.indexOf(_typesArray, rawFile.type) === -1) {
          ElMessage.error('请上传图片格式文件')
          return false
        }
        return true
      }

      const beforeFilesUpload: UploadProps['beforeUpload'] = (_) => {
        return true
      }

      const uploadImgFunctions = (options: UploadRequestOptions) => {

        // axios 上传时钩子
        const progress = function (progressEvent: any) {
          if (progressEvent.lengthComputable) {
            _cacheState.progress = Number((progressEvent.loaded / progressEvent.total * 100).toFixed(0))
            if (_cacheState.progress === 100) {
              if(options.file.name.endsWith('doc') || options.file.name.endsWith('docx')){
                _cacheState.status = '等待转换PDF'
              }else{
                _cacheState.status = '等待响应'
              }
              _cacheState.statusDot = 'i-success'
            }
          }
        }

        const _formData = new FormData()
        _formData.append('file', options.file)
        _cacheState.status = '上传中'
        _cacheState.statusDot = 'i-success'
        api.FileUpload(_formData,progress).then((res: any) => {
          if (!res.ResponseError) {
            console.log(res);
            props.form[fn.key!].push(res.data)
            _cacheState.status = '上传成功'
            _cacheState.statusDot = 'i-success'
          }else{
            _cacheState.status = '上传失败'
            _cacheState.statusDot = 'i-danger'
          }
        })
      }

      const PreviewDialog = () =>
          <el-dialog title='预览' v-model={_cacheState.previewVisible} appendToBody={true}>
            <div class='i-center'>
              <img w-full src={props.form[fn.key!]} alt="Preview Image"/>
            </div>
          </el-dialog>


      const IMGSlots = () => {
        return props.form[fn.key!] ?
            <>
              <el-image src={props.form[fn.key!]} class="upload-preview" preview-src-list={[props.form[fn.key!]]}/>
              <span class="upload-actions" onClick={(e: Event) => { e.stopImmediatePropagation(); }}>
              <span class="upload-list__item" onClick={() => { _cacheState.previewVisible = true }}>
                <el-icon><ZoomIn/></el-icon>
              </span>
              <span class="upload-list__item" onClick={() => { props.form[fn.key!] = '' }}>
                <el-icon><Delete/></el-icon>
              </span>
            </span>
            </> : <div class='upload-button-img dark'>
              <el-icon><Plus/></el-icon>
            </div>
      }

      const FileSlots = () => {
        return props.form[fn.key!].length !== 0 ?
            <div class='upload-actions-file'>
              <div v-show={!props.disabled && !fn.disabled} class='upload-button-files'>
                <el-icon><Plus/></el-icon>
                添加附件
              </div>
              {
                props.form[fn.key!].map((item: any, index: number) => {
                  return (
                      <div class='upload-list__item' onClick={(e: Event) => { e.stopImmediatePropagation(); }} key={index}>
                        <div class='left'>
                          <el-icon><Folder/></el-icon>
                          <a href={item.path} target="_blank" rel="noopener noreferrer">{item.name}</a>
                        </div>
                        <el-icon v-show={!props.disabled && !fn.disabled} class='right' onClick={() => { props.form[fn.key!].splice(index, 1) }}><Delete/></el-icon>
                      </div>
                  )
                })
              }
            </div> : <div v-show={!props.disabled && !fn.disabled} class='upload-button-files'>
              <el-icon><Plus/></el-icon>
              添加附件</div>
      }

      return (
          <>
            <PreviewDialog/>
            <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
              <el-upload
                  http-request={uploadImgFunctions}
                  name="file"
                  disabled={fn.disabled}
                  class='upload-components'
                  accept="*"
                  before-upload={fn.uploadConfig?.type === 'img' ? beforeAvatarUpload : beforeFilesUpload}
                  show-file-list={false}
                  v-slots={{default: () => fn.uploadConfig?.type === 'img' ? <IMGSlots/> : <FileSlots/>}}
              >
              </el-upload>
              <div class='i-between i-vcenter' style='width:100%;height:50px' v-show={_cacheState.status !== '待上传'}>
                <span> <span class={['i-dot', _cacheState.statusDot]}></span> {_cacheState.status}</span> <ElProgress style='width:90%;' percentage={_cacheState.progress}/>
              </div>
            </el-form-item>
          </>
      )
    }

    const selectMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-select
                style={{width: '100%'}}
                v-model={props.form[fn.key!]}
                disabled={fn.disabled}
                model-value={_getDeepByPath(props.form, fn.key!)}
                placeholder={fn.placeholder}
                onChange={(val: any) => { fn.optionsConfig?.onChange && fn.optionsConfig?.onChange(val, fn.options!, props.form, props.formFn) }}
                multiple={fn.optionsConfig?.multiple ? true : false}
                filterable
                clearable={fn.optionsConfig?.clearable ? true : false}
            >
              {
                fn.options!.map((item: any) => {
                  return <el-option label={item[fn.optionsConfig!.text]} value={item[fn.optionsConfig!.value]}/>
                })
              }
            </el-select>
          </el-form-item>
      )
    }


    const slotMapFn = <T extends IPopup.IFormFn>(fn: T) => {

      return (
          <span style={{width: fn.wrap || '100%', marginBottom: '16px'}}>
          {fn.slots && fn.slots({row: props.form, ref: formRef, disabled: props.disabled})}
        </span>
      )
    }


    const slotItemMapFn = <T extends IPopup.IFormFn>(fn: T) => {

      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            {fn.slots && fn.slots({row: props.form, ref: formRef, disabled: props.disabled})}
          </el-form-item>
      )
    }

    const dateMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-date-picker
                v-model={props.form[fn.key!]}
                type="date"
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
                placeholder={fn.placeholder}
                disabled={fn.disabled}
            />
          </el-form-item>
      )
    }
    const datetimeMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-date-picker
                v-model={props.form[fn.key!]}
                type="datetime"
                format="YYYY-MM-DD HH:mm:ss"
                value-format="YYYY-MM-DD HH:mm:ss"
                placeholder={fn.placeholder}
                disabled={fn.disabled}
            />
          </el-form-item>
      )
    }

    const yearMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <el-date-picker
                v-model={props.form[fn.key!]}
                type="year"
                format="YYYY"
                value-format="YYYY"
                placeholder={fn.placeholder}
                disabled={fn.disabled}
            />
          </el-form-item>
      )
    }

    const mapMapFn = <T extends IPopup.IFormFn>(fn: T) => {
      return (
          <el-form-item style={{width: fn.wrap || '100%'}} prop={fn.key} label={fn.label} rules={fn.rules}>
            <Map
                titleShow={fn.mapConfig?.titleShow || true}
                editShow={!props.disabled}
                title={props.form[fn.key!]}
                width={fn.wrap || '100%'}
                height={fn.mapConfig?.height}
                center={props.form[fn.mapConfig!.location] ? props.form[fn.mapConfig!.location].split(',') : [113.852640, 34.035506]}
                valChangeCallBack={(inputVal: string, center: number[]) => {
                  console.log(inputVal, center);
                  props.form[fn.key!] = inputVal
                  props.form[fn.mapConfig!.location] = center.join(',')
                  fn.mapConfig?.valChangeCallBack && fn.mapConfig?.valChangeCallBack(inputVal, center)
                }}
            />
          </el-form-item>
      )
    }

    const __typeMapFn: IPopup.ITypeMapFn = {
      input: inputMapFn,
      password: passwordMapFn,
      textarea: textareaMapFn,
      switch: switchMapFn,
      radio: radioMapFn,
      select: selectMapFn,
      date: dateMapFn,
      datetime: datetimeMapFn,
      year: yearMapFn,
      markdown: markdownMapFn,
      editor: editorMapFn,
      upload: uploadMapFn,
      itemSlots: slotItemMapFn,
      fieldSlots: slotMapFn,
      slots: slotMapFn,
      map: mapMapFn,
    }

    /**
     * @param { slots } Whether to render custom slot
     */
    const PopupSection = ({slots}: { slots: boolean }) => {
      return (
          <>
            {
              props.formFn.filter(<T extends IPopup.IFormFn>(fn: T) => {
                return slots ? (fn.type === 'slots') : (fn.type !== 'slots')
              }).map(<T extends IPopup.IFormFn>(fn: T) =>
                  fn.show ? (fn.show({row: props.form}) && __typeMapFn[fn.type!](fn)) : __typeMapFn[fn.type!](fn)
              )
            }
          </>
      )
    }

    /**
     * Dialog Footer Template
     */
    let _loading = ref<boolean>(false);
    const DialogFooter = (): JSX.Element => {

      const _submitClick = () => {
        _loading.value = true;
        const closeLoading = () => { _loading.value = false };
        formRef.value.validate((valid: boolean) => {
          if (valid) {
            emit('submitClick', closeLoading)
          } else {
            _loading.value = false
            ElNotification.error({
              message: '请检查表单是否填写完整',
            })
          }
        })
      }

      return (
          <>
            <div class='dialog-footer dark'>
              <ElButton text size='small' onClick={() => { props.beforeClose() }}>取消</ElButton>
              <ElButton type='primary' size='small' loading={_loading.value} onClick={_submitClick}>提交</ElButton>
            </div>
          </>
      )
    }


    return {resetFields, PopupSection, DialogFooter}
  },

  render(props: IPopup.IProps) {
    return (<div>
      {
        props.isDialog ?
            <ElDialog draggable appendToBody={true} lock-scroll={true} modelValue={props.show} title={props.title} width={props.width} before-close={props.beforeClose}>
              <ElForm class='comp-form' ref={formRef} model={props.form} disabled={props.disabled} labelWidth={props.labelWidth}>
                <this.PopupSection slots={false}/>
              </ElForm>
              <this.PopupSection slots={true}/>
              {props.footerShow && <this.DialogFooter/>}
            </ElDialog> :
            <>
              <ElForm class='comp-form' ref={formRef} model={props.form} disabled={props.disabled} labelWidth={props.labelWidth}>
                <this.PopupSection slots={false}/>
              </ElForm>
              <this.PopupSection slots={true}/>
            </>
      }
    </div>)
  }
});

</script>

<style lang="scss" scoped>

</style>